//++++++++++++++++++++++++++++++++++++++++++++
// ENBSeries effect file
// visit http://enbdev.com for updates
// Copyright (c) 2007-2017 Boris Vorontsov
//++++++++++++++++++++++++++++++++++++++++++++



//+++++++++++++++++++++++++++++
//internal parameters, can be modified
//+++++++++++++++++++++++++++++
float	EFresnelFactor
<
	string UIName="Vehicle: FresnelFactor";
	string UIWidget="Spinner";
	float UIMin=1.0;
	float UIMax=16.0;
> = {4.0};



//+++++++++++++++++++++++++++++
//external parameters, do not modify
//+++++++++++++++++++++++++++++
//keyboard controlled temporary variables (in some versions exists in the config file). Press and hold key 1,2,3...8 together with PageUp or PageDown to modify. By default all set to 1.0
float4	tempF1; //0,1,2,3
float4	tempF2; //5,6,7,8
float4	tempF3; //9,0
//x=Width, y=1/Width, z=ScreenScaleY, w=1/ScreenScaleY
float4	ScreenSize;
//changes in range 0..1, 0 means that night time, 1 - day time
float	ENightDayFactor;
//changes 0 or 1. 0 means that exterior, 1 - interior
float	EInteriorFactor;
//.x - current weather index, .y - incoming weather index, .z - weather transition, .w - time of the day in 24 standart hours. Weather index is value from _weatherlist.ini, for example WEATHER002 means index==2, but index==0 means that weather not captured.
float4	WeatherAndTime;
//x=generic timer in range 0..1, period of 16777216 ms (4.6 hours), w=frame time elapsed (in seconds)
float4	Timer;
//additional info for computations
//float4	TempParameters; 
//fov in degrees
float	FieldOfView;
//time in 0..24 format
float	GameTime;
//sun direction in world space
float4	SunDirection; //.w - 1 when sun is set
//constants set in enbhelper.dll, can be anything captured from game
float4	CustomShaderConstants1[8];


//transposed transform matrix view*projection and inverse of it
float4	MatrixVP[4];
float4	MatrixInverseVP[4];
float4	MatrixVPRotation[4];
float4	MatrixInverseVPRotation[4];
float4	MatrixView[4];
float4	MatrixInverseView[4];
float4	CameraPosition;


float4x4	MatrixWVP;
float4x4	MatrixWVPInverse;
float4x4	MatrixWorld;
float4x4	MatrixProj;
float4	diffColor; //material diffuse color
float4	specColor; //material specular color
float4	ambColor; //material ambient color (emissive seems unused)
float4	FogParam; //x - nearclip, y - farclip, z - fog start, w - fog end
float4	FogFarColor;
float4	lightDiffuse[8];
float4	lightSpecular[8];
float4	lightDirection[8];
float4	VehicleParameters1; //x - GlassReflectionAmount, y - is alpha blend, z - ReflectionAmount (with some internal factor), w - opacity factor


texture2D texOriginal; //diffuse color
texture2D texRefl; //reflection
texture2D texEnv; //sky blurred

sampler2D SamplerOriginal = sampler_state
{
	Texture   = <texOriginal>;
};

sampler2D SamplerRefl = sampler_state
{
	Texture   = <texRefl>;
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = LINEAR;
	AddressU  = Mirror;//Clamp;
	AddressV  = Mirror;//Clamp;
	SRGBTexture=FALSE;
	MaxMipLevel=0;
	MipMapLodBias=0;
};

sampler2D SamplerEnv = sampler_state
{
	Texture   = <texEnv>;
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = LINEAR;
	AddressU  = Clamp;
	AddressV  = Clamp;
	SRGBTexture=FALSE;
	MaxMipLevel=0;
	MipMapLodBias=0;
};

struct PS_OUTPUT3
{
	float4 Color[3] : COLOR0;
};

struct VS_INPUT_N
{
	float3	pos : POSITION;
	float3	normal : NORMAL;
	float2	txcoord0 : TEXCOORD0;
};

struct VS_OUTPUT
{
	float4	pos : POSITION;
	float2	txcoord0 : TEXCOORD0;
	float3	viewnormal : TEXCOORD3;
	float3	eyedir : TEXCOORD4;
	float3	wnormal : TEXCOORD5;
	float4	vposition : TEXCOORD6;
	float3	normal : TEXCOORD7;
};



//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
VS_OUTPUT VS_Draw(VS_INPUT_N IN)
{
    VS_OUTPUT OUT;

	float4	pos;
	pos.xyz=IN.pos.xyz;
	pos.w=1.0;

	float4	tpos;
	tpos=mul(pos, MatrixWVP);

	OUT.pos=tpos;
	OUT.vposition=tpos;
	OUT.txcoord0=IN.txcoord0;

	float3	wnormal=normalize(mul(IN.normal.xyz, MatrixWorld));
	float3	normal;
	normal.x=dot(wnormal.xyz, MatrixView[0]);
	normal.y=dot(wnormal.xyz, MatrixView[1]);
	normal.z=dot(wnormal.xyz, MatrixView[2]);

	OUT.viewnormal=normalize(normal.xyz);
	OUT.normal=normalize(mul(IN.normal.xyz, MatrixWVP));
	OUT.wnormal=wnormal;

	float3	campos=CameraPosition;
	//campos.x=MatrixInverseView[0].w;
	//campos.y=MatrixInverseView[1].w;
	//campos.z=MatrixInverseView[2].w;
	OUT.eyedir=(mul(pos, MatrixWorld) - campos);

    return OUT;
}


//some of the code could be trash accumulated from old versions with dirty tricks
PS_OUTPUT3 PS_Draw(VS_OUTPUT IN, in float2 vpos : VPOS)
{
	float4	fakeparam1;
	//trying to detect chrome
	fakeparam1.xyz=diffColor*specColor*specColor.w*0.085;
	//fakeparam1.w=saturate(dot(fakeparam1.xyz, 0.333));
	fakeparam1.w=min(fakeparam1.x, min(fakeparam1.y, fakeparam1.z));
	fakeparam1.w=saturate(1.0-fakeparam1.w);
	fakeparam1.w*=fakeparam1.w;


	float3	normal=normalize(IN.normal.xyz);
	float3	wnormal=normalize(IN.wnormal.xyz);
	float3	eyedir;
	float3	weyedir;
	float4	res;
	float4	reflectioncolor;
	float4	texcolor;
	float4	color;
	float4	coord;
	float2	refluv;
	coord.zw=0.0;

	coord.w=fakeparam1.w*4.0; //make here own lod control

	eyedir=normalize(-IN.vposition.xyz);
	weyedir=normalize(-IN.eyedir.xyz);

	texcolor=tex2D(SamplerOriginal, IN.txcoord0.xy); //diffuse texture

	float	opacity=saturate(texcolor.a*diffColor.a);

	refluv.xy=(IN.vposition.xy /IN.vposition.w)*float2(0.5, -0.5) + 0.5;

	float3	refl=reflect(eyedir, normal);

	refluv.xy+=(refl.xy*float2(-1.0, 1.0)*0.5); //don't use too long vector which go outside of texture often

	//read reflection texture with little blurring
	float2	aaoffsets[4]=
	{
		float2(-0.683, 0.183),
		float2(-0.183,-0.683),
		float2( 0.683, 0.183),
		float2( 0.183, 0.683)
	};
	float2	ddxrefluv=ddx(refluv);
	float2	ddyrefluv=ddy(refluv);
	float2	texeloffset;
	texeloffset.y=length(ddxrefluv);
	texeloffset.x=length(ddyrefluv);
	reflectioncolor=0.0;
	for	(int i=0; i<4; i++)
	{
		coord.xy=refluv + texeloffset*aaoffsets[i];
		reflectioncolor+=tex2Dlod(SamplerRefl, coord);
	}
	reflectioncolor*=0.25;

	//sky texture from top projected
	coord.y=1.0-((wnormal.z*0.5)+0.5);
	coord.x=0.5;
	float4	skylight=tex2Dlod(SamplerEnv, coord);


	//mix sky and screen reflection to correct bugs when looking from top to bottom (most likely sky is reflected)
	float	skyreflmix;
	//skyreflmix=saturate(abs(wnormal.z)*abs(normal.z));//v1
	skyreflmix=saturate(wnormal.z)*saturate(-normal.z);//v2
	skyreflmix*=skyreflmix;
	reflectioncolor.xyz=lerp(reflectioncolor, skylight, skyreflmix);

	float3	origreflectioncolor=reflectioncolor;


	//environment reflection multiplier
	float	colorfact=max(texcolor.r, max(texcolor.g, texcolor.b));
	float	difffact=dot(diffColor.xyz, 0.333);
	float	opacityfact=saturate(diffColor.a*diffColor.a*diffColor.a*texcolor.a);

	//make angle dependent
	float	normalfact=saturate(1.0+skyreflmix*0.3 -abs(normal.z));
	float	fresnelfact=pow(normalfact, EFresnelFactor);
	fresnelfact=lerp(fresnelfact, 0.0, 0.95*saturate(colorfact*difffact*opacityfact));//for bright cars reduce reflection, but wrong for white glass
	reflectioncolor.xyz*=fresnelfact;
	//disable ugly rectangles when the car appears
	//reflectioncolor.xyz*=VehicleParameters1.w;

	//not work properly, because not strict opacity factor
	float3	nontransparentrefl=reflectioncolor.xyz*VehicleParameters1.z; //reflection amount variable with some complex internal calculation to reduce bugs
	nontransparentrefl.xyz=min(nontransparentrefl, origreflectioncolor);//limit reflections

	//increase for transparent
	float3	transparentrefl=reflectioncolor.xyz*VehicleParameters1.x/saturate(opacity + 0.02 + VehicleParameters1.y);//is transparent
	reflectioncolor.xyz=lerp(transparentrefl, nontransparentrefl, saturate(opacity+VehicleParameters1.y));//is transparent

	//distance fade of reflection, disabling some artifacts
	float	distfade=saturate(IN.vposition.z * 0.014);
	reflectioncolor.xyz*=1.0-distfade*distfade;

	float	specpow=specColor.w / max(opacityfact*opacityfact*opacityfact, 0.01);//increase for glass because colors are low dynamic range

	//compute lighting for 8 lights
	float3	lightingcolor=0.0;
	float3	specularcolor=0.0;
	for (int li=0; li<8; li++)
	{
		lightingcolor.xyz+=lightDiffuse[li] * saturate(dot(lightDirection[li].xyz, wnormal.xyz));

		//trick to boost night street lights only
		//lightingcolor.xyz+=saturate(lightDiffuse[li]-lightSpecular[li]) * saturate(dot(lightDirection[li].xyz, wnormal.xyz));

		float3	tvec=normalize(lightDirection[li].xyz+weyedir.xyz);
		float	specfact=saturate(dot(tvec, wnormal.xyz));
		specfact=pow(specfact, specpow);
		specularcolor+=lightDiffuse[li] * specfact;
		//specularcolor+=saturate(lightDiffuse[li]-lightSpecular[li]) * specfact; //at night use only street lights
		//specularcolor+=lightSpecular[li] * specfact; //not applied for all lights
	}
	specularcolor*=specColor;
	specularcolor*=diffColor.a;//reduce specular for glass

	//simple ibl based on sky map
	skyreflmix=saturate(wnormal.z*0.5+0.5);
	skyreflmix=saturate(skyreflmix*skyreflmix*0.3); //ibl amount
	lightingcolor.xyz+=skylight*skyreflmix;

	//mix everything tex*diff(ambient+point+sunlight+specular)+reflection; almost prehistoric classic
	//car lights in ambColor
	//v1 clamped car lights like in original game (no hdr for them)
	//float3	lighting=(lightingcolor*diffColor + saturate(ambColor))*texcolor;
	//v2 no clamping of car lights
	float	grayspec=max(specColor.x, max(specColor.y, specColor.z));
	grayspec=saturate(grayspec);
	float3	lighting=(lightingcolor*diffColor + ambColor)*texcolor;
	res.xyz=lighting + reflectioncolor*grayspec + specularcolor*diffColor;

	res.w=opacity;

	//apply fog
	float	fadefact=(FogParam.w - IN.vposition.z) / (FogParam.w - FogParam.z);
	res.xyz=lerp(FogFarColor.xyz, res.xyz, saturate(fadefact));

	//write defer normal and depth for using in other effects. Alpha of normal is mask for something useful
	float3	ssnormal=normalize(IN.viewnormal);
	ssnormal.yz=-ssnormal.yz;
	float4	res1;
	float4	res2;
	res1.xyz=ssnormal*0.5+0.5;
	//depth
	float	nonlineardepth=(IN.vposition.z/IN.vposition.w);
	//253.0/255.0 are peds, 255.0/255.0 scene. You can define own mask value which will be available in alpha of normal map
	res1.w=254.0/255.0; //mask
	//example for output of back lights to mask, can be used to boost intensity of bloom for example
	//float	islight=max(ambColor.x, max(ambColor.y, ambColor.z));
	//if (islight>1.0) res1.w=253.0/255.0;
	//if transparent, write it value to mask. But maybe better to skip writing by COLORWRITEENABLE1=0?

	//trick to make motion blur for lights
//	float	grayamb=max(ambColor.x, max(ambColor.y, ambColor.z));
//	if (grayamb>1.0) opacity=0.0; else opacity=1.0;
//	if (opacity<0.999) res1.w=opacity; //comment, if you want glass to not be drawed to normal and also set ZWriteEnable=FALSE in DrawTransparent

	res2=nonlineardepth; //depth
//	res2.w=1.0; //uncomment, if you want glass to be drawed to depth properly
	PS_OUTPUT3	OUT;
	OUT.Color[0]=res;
	OUT.Color[1]=res1;
	OUT.Color[2]=res2;
	return OUT;
}



//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
technique Draw
{
    pass p0
    {
	VertexShader = compile vs_3_0 VS_Draw();
	PixelShader  = compile ps_3_0 PS_Draw();
	}
}


//most likely car's windows, but can be some buggy elements
technique DrawTransparent
{
    pass p0
    {
	VertexShader = compile vs_3_0 VS_Draw();
	PixelShader  = compile ps_3_0 PS_Draw();
	//maybe just ignore defer for transparent objects?
//	COLORWRITEENABLE1=0;
//	COLORWRITEENABLE2=0;
//	ZWriteEnable=FALSE;
	}
}

